package com.gengzp.common.appender;

import com.gengzp.common.context.TraceContextHolder;
import com.gengzp.common.utils.TraceUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;

/**
 * Log4j2 自定义 Appender：输出日志到控制台的同时，统一存入 TraceUtils 全局队列（供定时任务落库）
 */
@Plugin(name = "Log4jAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
public class Log4jAppender extends AbstractAppender {

    // 构造方法
    protected Log4jAppender(String name, Layout<? extends Serializable> layout, boolean ignoreExceptions, Property[] properties) {
        super(name, null, layout, ignoreExceptions, properties);
    }

    /**
     * 处理日志事件的方法：所有线程日志统一存入 TraceUtils 全局队列
     */
    @Override
    public void append(LogEvent event) {
        // 1. 无 traceId 或事件为空，直接跳过
        String traceId = TraceContextHolder.getTraceId();
        if (StringUtils.isBlank(traceId) || event == null) {
            return;
        }

        // 2. 格式化日志消息
        Layout<? extends Serializable> layout = getLayout();
        if (layout == null) {
            return;
        }
        byte[] logBytes = layout.toByteArray(event);
        String logMessage = new String(logBytes, StandardCharsets.UTF_8);

        try {
            // 所有线程日志统一存入 TraceUtils 全局队列，不再区分线程类型
            TraceUtils.addTraceLog(traceId, logMessage);
        } catch (Exception e) {
            // 处理异常，避免影响主程序执行
            if (!ignoreExceptions()) {
                throw new RuntimeException("Failed to append log to TraceUtils", e);
            }
        }
    }

    /**
     * 插件工厂方法，用于创建Appender实例
     */
    @PluginFactory
    public static Log4jAppender createAppender(
            @PluginAttribute("name") String name,
            @PluginAttribute("ignoreExceptions") boolean ignoreExceptions,
            @PluginElement("Layout") Layout<? extends Serializable> layout,
            @PluginElement("Properties") Property[] properties) {

        // 默认布局：保留原有格式，确保日志可读性
        if (layout == null) {
            layout = PatternLayout.newBuilder()
                    .withPattern("[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%p] {%F:%L} - %m%n")
                    .build();
        }

        // 校验名称非空
        if (name == null) {
            LOGGER.error("Log4jAppender名称不能为空");
            return null;
        }

        return new Log4jAppender(name, layout, ignoreExceptions, properties);
    }
}